﻿namespace EggOfPanKu.Library.Objects
{
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Linq;
    using System.Text;
    //    using System.Xml;
    using System.Xml.Linq;
    using EggOfPanKu.Library.Infrastructure;

    public class GameDeck
    {
        #region Fields

        /// <summary>
        /// Name of the Deck
        /// </summary>
        private string _deckName = string.Empty;

        /// <summary>
        /// Deck containing the Dynasty Cards
        /// </summary>
        private Deck _dynastyDeck = new Deck(CardDeck.Dynasty);

        /// <summary>
        /// Deck containing the Fate Cards
        /// </summary>
        private Deck _fateDeck = new Deck(CardDeck.Fate);

        /// <summary>
        /// Deck containing the Starts In Play Cards
        /// </summary>
        private Deck _startDeck = new Deck(CardDeck.StartsInPlay);

        /// <summary>
        /// FileName associated with the Deck
        /// </summary>
        private string _fileName = string.Empty;

        /// <summary>
        /// XML Document describing the GameDeck (used in Saving/Loading Decks)
        /// </summary>
        private XDocument _gameDeckXmlDoc = new XDocument();

        #endregion Fields

        #region Constructors

        /// <summary>
        /// Initializes a new instance of the <see cref="GameDeck"/> class.
        /// </summary>
        /// Created by Paige on 10/10/2009
        public GameDeck()
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="GameDeck"/> class.
        /// </summary>
        /// <param name="fileName">Name of the file.</param>
        /// Created by Paige on 10/10/2009
        public GameDeck(string fileName)
        {
            LoadDeck(fileName);
        }

        #endregion Constructors

        #region Properties

        /// <summary>
        /// Returns all the cards in the Game Deck
        /// </summary>
        public List<Card> Cards
        {
            get
            {
                List<Card> allCards = new List<Card>();
                allCards.AddRange(DynastyDeck.Cards);
                allCards.AddRange(FateDeck.Cards);
                allCards.AddRange(StartDeck.Cards);
                allCards.Add(StrongholdCard);
                return allCards.OrderBy(c => c.Name).ToList();
            }
        }

        /// <summary>
        /// Gets or sets the name of the deck.
        /// </summary>
        /// <value>The name of the deck.</value>
        /// Created by Paige on 10/10/2009
        public string DeckName
        {
            get
            {
                return _deckName;
            }
            set
            {
                _deckName = value;
            }
        }

        /// <summary>
        /// Gets the dynasty deck.
        /// </summary>
        /// <value>The dynasty deck.</value>
        /// Created by Paige on 10/10/2009
        public Deck DynastyDeck
        {
            get { return _dynastyDeck; }
        }

        public int DynastyCount
        {
            get { return _dynastyDeck.Cards.Count; }
        }

        /// <summary>
        /// Gets the fate deck.
        /// </summary>
        /// <value>The fate deck.</value>
        /// Created by Paige on 10/10/2009
        public Deck FateDeck
        {
            get { return _fateDeck; }
        }

        public int FateCount
        {
            get { return _fateDeck.Cards.Count; }
        }

        /// <summary>
        /// Returns the Starts In Play deck
        /// </summary>
        public Deck StartDeck
        {
            get { return _startDeck; }
        }

        /// <summary>
        /// Gets or sets the name of the file.
        /// </summary>
        /// <value>The name of the file.</value>
        /// Created by Paige on 10/10/2009
        public string FileName
        {
            get
            {
                return _fileName;
            }
            set
            {
                _fileName = value;
            }
        }

        /// <summary>
        /// Gets or sets the game deck XML doc.
        /// </summary>
        /// <value>The game deck XML doc.</value>
        /// Created by Paige on 10/10/2009
        public XDocument GameDeckXmlDoc
        {
            get { return _gameDeckXmlDoc; }
            set { _gameDeckXmlDoc = value; }
        }

        /// <summary>
        /// Gets or sets the stronghold card.
        /// </summary>
        /// <value>The stronghold card.</value>
        /// Created by Paige on 10/10/2009
        public Stronghold StrongholdCard
        {
            get
            {
                return (Stronghold)StartDeck.Cards.SingleOrDefault(card => card.CardType == CardType.Stronghold);
            }
        }

        #endregion Properties

        #region Methods

        /// <summary>
        /// Add Card(s) from the CardDB based on ID
        /// </summary>
        /// <param name="CardID">ID of the Card(s) being added</param>
        /// <param name="Count">Number of Cards to Add</param>
        public void AddCard(string CardID, int Count)
        {
            Card card = CardDatabase.FindCardByID(CardID);
        }

        /// <summary>
        /// Add Card(s) to the GameDeck Deck objects
        /// </summary>
        /// <param name="NewCard">Card to Add</param>
        /// <param name="Count">Number of times to add the Card</param>
        public string AddCard(Card NewCard, int Count)
        {
            string error = "";
            for (int i = 0; i < Count; i++)
            {
                if(NewCard.Name.StartsWith("Bamboo Harvesters"))
                {
                    Card existing = StartDeck.Cards.SingleOrDefault(c => c.Name.StartsWith("Bamboo Harvesters"));
                    if (existing == null)
                    {
                        StartDeck.Cards.Add(NewCard);
                    }
                    else
                    {
                        error = "Only 1 copy of Bamboo Harvesters or Bamboo Harvesters - Experienced is allowed.";
                    }
                    break;
                }

                if (NewCard.Name.StartsWith("Border Keep"))
                {
                    Card existing = StartDeck.Cards.SingleOrDefault(c => c.Name.StartsWith("Border Keep"));
                    if (existing == null)
                    {
                        StartDeck.Cards.Add(NewCard);
                    }
                    else
                    {
                        error = "Only 1 copy of Border Keep or Border Keep - Experienced is allowed.";
                    }

                    break;
                }

                switch (NewCard.DeckType)
                {
                    case CardDeck.Dynasty:
                        if (DynastyDeck.Cards.Contains(NewCard))
                            DynastyDeck.Cards.Single(card => card.CardID == NewCard.CardID).CountInDeck++;
                        else 
                            DynastyDeck.Cards.Add(NewCard);
                        break;
                    case CardDeck.Fate:
                        if (FateDeck.Cards.Contains(NewCard))
                            FateDeck.Cards.Single(card => card.CardID == NewCard.CardID).CountInDeck++;
                        else
                            FateDeck.Cards.Add(NewCard);
                        break;
                    case CardDeck.StartsInPlay:
                        if (StartDeck.Cards.Contains(NewCard))
                            StartDeck.Cards.Single(card => card.CardID == NewCard.CardID).CountInDeck++;
                        else
                            StartDeck.Cards.Add(NewCard);
                        break;
                    default:
                        Stronghold old = (Stronghold)StartDeck.Cards.SingleOrDefault(card => card.CardType == CardType.Stronghold);
                        if (old == null)
                        {
                            StartDeck.Cards.Add(NewCard);
                        }
                        else
                        {
                            StartDeck.Cards.Remove(old);
                            StartDeck.Cards.Add(NewCard);
                        }
                        break;
                }
            }

            return error;
        }

        /// <summary>
        /// Decks as text.
        /// </summary>
        /// <returns></returns>
        /// Created by Paige on 10/10/2009
        public string DeckAsText()
        {
            //Initialize Stringbuilder
            StringBuilder cards = new StringBuilder();
            
            //Loop through the DynastyDeck and Append Cards
            foreach (Card card in DynastyDeck.Cards.Distinct(new CardComparer()).OrderBy(c => c.Name))
            {
                cards.Append(String.Format("{0}x {1}{2}", card.CountInDeck, card.Name, System.Environment.NewLine));
            }
            string DynastyText = String.Format("# Dynasty - {0}{1}{2}", DynastyDeck.Cards.Sum(card => card.CountInDeck), System.Environment.NewLine, cards);

            cards.Clear();
            //Loop through the FateDeck and Append Cards
            foreach (Card card in FateDeck.Cards.Distinct(new CardComparer()).OrderBy(c => c.Name))
            {
                cards.Append(String.Format("{0}x {1}{2}", card.CountInDeck, card.Name, System.Environment.NewLine));
            }
            string FateText = String.Format("# Fate - {0}{1}{2}", FateDeck.Cards.Sum(card => card.CountInDeck), System.Environment.NewLine, cards);

            cards.Clear();
            //Loop through the StartDeck and Append Cards
            foreach(Card card in StartDeck.Cards.Distinct(new CardComparer()).OrderBy(c => c.Name)) 
            {
                cards.Append(String.Format("{0}x {1}{2}", card.CountInDeck, card.Name, Environment.NewLine));
            }
            string StartText = String.Format("# Starts In Play - {0}{1}{2}", StartDeck.Cards.Sum(card => card.CountInDeck), Environment.NewLine, cards);

            //Return the Deck as a String
            return String.Format("{1}{0}{0}{2}{0}{3}{0}{4}", Environment.NewLine,
                String.Format("Deck: {0}", this.DeckName),
                StartText.ToString(),
                DynastyText.ToString(),
                FateText.ToString());
        }

        /// <summary>
        /// Loads the deck.
        /// </summary>
        /// <param name="FilePath">The file path.</param>
        /// Created by Paige on 10/10/2009
        public void LoadDeck(string FilePath)
        {
            try
            {
                //Check to see if File is XML
                XElement xmlDoc = XElement.Load(FilePath);
                LoadXMLDeck(xmlDoc);
            }
            catch (System.Exception exc)
            {
                LogManager.Write(LogManager.Level.WARN, String.Format("Unable to load XML Deck {0}: {1}. Attempting to load TXT Deck.",
                    FilePath, exc.Message), "GameDeck::LoadDeck");
                //Loading as XML failed, so load as Text
                LoadTextDeck(FilePath);
            }
        }

        /// <summary>
        /// Remove Card(s) from the appropriate Deck
        /// </summary>
        /// <param name="OldCard">ID of the Card(s) to be removed</param>
        /// <param name="Count">Number of Cards to Remove</param>
        public void RemoveCard(Card OldCard, int Count)
        {
            switch (OldCard.DeckType)
            {
                case CardDeck.Dynasty:
                    if (DynastyDeck.Cards.Contains(OldCard))
                    {
                        if (OldCard.CountInDeck <= Count)
                            DynastyDeck.Cards.Remove(OldCard);
                        else
                            OldCard.CountInDeck -= Count;
                    }
                    
                    break;
                case CardDeck.Fate:
                    if (FateDeck.Cards.Contains(OldCard))
                    {
                        if (OldCard.CountInDeck <= Count)
                            FateDeck.Cards.Remove(OldCard);
                        else
                            OldCard.CountInDeck -= Count;
                    }
                    break;
                case CardDeck.StartsInPlay:
                    if (StartDeck.Cards.Contains(OldCard))
                    {
                        if (OldCard.CountInDeck <= Count)
                            StartDeck.Cards.Remove(OldCard);
                        else
                            OldCard.CountInDeck -= Count;
                    }
                    break;
                default:
                    if (StartDeck.Cards.Contains(OldCard))
                    {
                        StartDeck.Cards.Remove(OldCard);
                    }
                    break;
            }
        }

        /// <summary>
        /// Saves the deck.
        /// </summary>
        /// Created by Paige on 10/10/2009
        public string SaveDeck()
        {
            try
            {
                String strVersion = System.Reflection.Assembly.GetCallingAssembly().GetName().Version.ToString();

                //Initialize DeckXML
                GameDeckXmlDoc = new XDocument(
                    new XComment(string.Format("EggOfPanKu v{0}", strVersion)),
                    new XComment(string.Format("This deck file was last saved on {0}", DateTime.Now.ToShortDateString())),
                    new XElement("Deck",
                        new XAttribute("name", DeckName),
                        GetDynastyXml(),
                        GetFateXml(),
                        GetStartsInPlayXml()
                        ));

                //Write XML
                GameDeckXmlDoc.Save(FileName);
                return "";
            }
            catch (System.Exception exc)
            {
                return exc.Message;
            }
        }

        private XElement GetStartsInPlayXml()
        {
            

            XElement startXml = new XElement("StartsInPlay",
                new XAttribute("cards", StartDeck.Cards.Count.ToString()),
                GetStrongholdXml(),
                GetCardTypeXml(CardType.Wind, StartDeck),
                GetCardTypeXml(CardType.Holding, StartDeck)
                );
            
            return startXml;
        }

        /// <summary>
        /// Gets the card type XML.
        /// </summary>
        /// <param name="cardType">Type of the card.</param>
        /// <param name="deck">The deck.</param>
        /// <returns></returns>
        /// Created by Paige on 10/10/2009
        private XElement GetCardTypeXml(CardType cardType, Deck deck)
        {
            var cards = from c in deck.Cards
                        where c.CardType == cardType
                        select c;
            List<XElement> setsList = new List<XElement>();
            foreach (var card in cards)
            {
                setsList.Add(GetCardXml((Card)card, card.CountInDeck));
            }

            XElement cardTypeRoot = new XElement(cardType.ToString(), setsList);

            return cardTypeRoot;
        }

        /// <summary>
        /// Gets the card XML.
        /// </summary>
        /// <param name="card">The card.</param>
        /// <returns></returns>
        /// Created by Paige on 10/10/2009
        private XElement GetCardXml(Card card, int Count = 1)
        {
            XElement cardXml = new XElement("Card",
                new XAttribute("id", card.CardID),
                new XAttribute("count", Count),
                new XCData(card.Name)
                );
            return cardXml;
        }

        /// <summary>
        /// Gets the dynasty XML.
        /// </summary>
        /// <returns></returns>
        /// Created by Paige on 10/10/2009
        private XElement GetDynastyXml()
        {
            XElement dynastyXml = new XElement("Dynasty",
            new XAttribute("cards", DynastyDeck.Cards.Count.ToString()),
            GetCardTypeXml(CardType.Celestial, DynastyDeck),
            GetCardTypeXml(CardType.Event, DynastyDeck),
            GetCardTypeXml(CardType.Holding, DynastyDeck),
            GetCardTypeXml(CardType.Personality, DynastyDeck),
            GetCardTypeXml(CardType.Region, DynastyDeck));

            return dynastyXml;
        }

        /// <summary>
        /// Gets the fate XML.
        /// </summary>
        /// <returns></returns>
        /// Created by Paige on 10/10/2009
        private XElement GetFateXml()
        {
            XElement fateXml = new XElement("Fate",
            new XAttribute("cards", FateDeck.Cards.Count.ToString()),
            GetCardTypeXml(CardType.Ancestor, FateDeck),
            GetCardTypeXml(CardType.Strategy, FateDeck),
            GetCardTypeXml(CardType.Follower, FateDeck),
            GetCardTypeXml(CardType.Item, FateDeck),
            GetCardTypeXml(CardType.Ring, FateDeck),
            GetCardTypeXml(CardType.Spell, FateDeck),
            GetCardTypeXml(CardType.Sensei, FateDeck));

            return fateXml;
        }

        /// <summary>
        /// Gets the stronghold XML.
        /// </summary>
        /// <returns></returns>
        /// Created by Paige on 10/10/2009
        private XElement GetStrongholdXml()
        {
            XElement strongholdXml = new XElement("StrongHold");
            if (StrongholdCard != null)
            {
                strongholdXml.Add(GetCardXml(StrongholdCard));
            }

            return strongholdXml;
        }

        /// <summary>
        /// Loads the text deck.
        /// </summary>
        /// <param name="FilePath">The file path.</param>
        /// Created by Paige on 10/10/2009
        private void LoadTextDeck(string FilePath)
        {
            try
            {
                using (StreamReader tr = File.OpenText(FilePath))
                {
                    while (!tr.EndOfStream)
                    {
                        //read a trimmed string
                        string lineData = tr.ReadLine().Trim(); ;

                        //if it's empty or has a "#" (comment) then discard it
                        if (!string.IsNullOrEmpty(lineData) && !lineData.StartsWith("#"))
                        {
                            //split on the

                            string[] cardData = lineData.Split(new char[] { ' ' }, 2, StringSplitOptions.RemoveEmptyEntries);
                            Card card = CardDatabase.FindCardByName(cardData[1]);
                            int cardCount = int.Parse(cardData[0]);
                            AddCard(card, cardCount);
                        }
                    }
                }
            }
            catch (System.Exception exc)
            {
                LogManager.Write(LogManager.Level.ERROR, String.Format("Unable to load TXT Deck: {0}", exc.Message), "GameDeck::LoadTextDeck");
            }
        }

        /// <summary>
        /// Loads the XML deck.
        /// </summary>
        /// <param name="deckXml">The deck XML.</param>
        /// Created by Paige on 10/10/2009
        private void LoadXMLDeck(XElement deckXml)
        {
            try
            {
                //Set DeckName
                DeckName = deckXml.Attribute("name").Value;
                
                foreach (XElement card in deckXml.Descendants("Card"))
                {
                    AddCard(CardDatabase.FindCardByID(card.Attribute("id").Value),
                            Int32.Parse(card.Attribute("count").Value));
                }

            }
            catch (System.Exception exc)
            {
                LogManager.Write(LogManager.Level.ERROR, String.Format("Error loading XML Deck: {0}", exc.Message), "GameDeck::LoadXMLDeck");
            }
        }

        #endregion Methods
    }
}